/*
 * Copyright 2019 Flipkart Internet Pvt. Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.flipkart.ohos.proteus.parser;

import ohos.agp.colors.RgbPalette;
import ohos.agp.components.Component;
import ohos.agp.components.DependentLayout;
import ohos.agp.components.Image;
import ohos.agp.components.Text;
import ohos.agp.text.Font;
import ohos.agp.utils.Color;
import ohos.agp.utils.LayoutAlignment;
import ohos.global.systemres.ResourceTable;

import com.flipkart.ohos.proteus.LogUtil;
import com.flipkart.ohos.proteus.ProteusConstants;
import com.flipkart.ohos.proteus.value.Primitive;
import com.flipkart.ohos.proteus.value.Value;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

/**
 * @author kiran.kumar
 */
public class ParseHelper {
    private static final String TAG = "ParseHelper";
    private static final String FALSE = "false";
    private static final String VISIBLE = "visible";
    private static final String INVISIBLE = "invisible";
    private static final String HIDE = "hide";
    private static final String CENTER = "center";
    private static final String CENTER_HORIZONTAL = "center_horizontal";
    private static final String CENTER_VERTICAL = "center_vertical";
    private static final String LEFT = "left";
    private static final String RIGHT = "right";
    private static final String TOP = "top";
    private static final String BOTTOM = "bottom";
    private static final String START = "start";
    private static final String END = "end";
    private static final String MIDDLE = "middle";
    private static final String BEGINNING = "beginning";
    private static final String MARQUEE = "marquee";

    private static final String BOLD = "bold";
    private static final String ITALIC = "italic";
    private static final String BOLD_ITALIC = "bold|italic";

    private static final String TEXT_ALIGNMENT_INHERIT = "inherit";
    private static final String TEXT_ALIGNMENT_GRAVITY = "gravity";
    private static final String TEXT_ALIGNMENT_CENTER = "center";
    private static final String TEXT_ALIGNMENT_TEXT_START = "start";
    private static final String TEXT_ALIGNMENT_TEXT_END = "end";
    private static final String TEXT_ALIGNMENT_VIEW_START = "viewStart";
    private static final String TEXT_ALIGNMENT_VIEW_END = "viewEnd";

    private static final String TWEEN_LOCAL_RESOURCE_STR = "@anim/";

    private static final Map<Integer, Primitive> sVisibilityMap = new HashMap<>();
    private static final Map<String, Primitive> sGravityMap = new HashMap<>();
    private static final Map<String, Integer> sDividerMode = new HashMap<>();
    private static final Map<String, Enum> sEllipsizeMode = new HashMap<>();
    private static final Map<String, Integer> sVisibilityMode = new HashMap<>();
    private static final Map<String, Integer> sTextAlignment = new HashMap<>();
    private static final Map<String, Image.ScaleMode> sImagescaleMode = new HashMap<>();

    static {
        sVisibilityMap.put(Component.VISIBLE, new Primitive(Component.VISIBLE));
        sVisibilityMap.put(Component.INVISIBLE, new Primitive(Component.INVISIBLE));
        sVisibilityMap.put(Component.HIDE, new Primitive(Component.HIDE));

        sGravityMap.put(CENTER, new Primitive(LayoutAlignment.CENTER));
        sGravityMap.put(CENTER_HORIZONTAL, new Primitive(LayoutAlignment.HORIZONTAL_CENTER));
        sGravityMap.put(CENTER_VERTICAL, new Primitive(LayoutAlignment.VERTICAL_CENTER));
        sGravityMap.put(LEFT, new Primitive(LayoutAlignment.LEFT));
        sGravityMap.put(RIGHT, new Primitive(LayoutAlignment.RIGHT));
        sGravityMap.put(TOP, new Primitive(LayoutAlignment.TOP));
        sGravityMap.put(BOTTOM, new Primitive(LayoutAlignment.BOTTOM));
        sGravityMap.put(START, new Primitive(LayoutAlignment.START));
        sGravityMap.put(END, new Primitive(LayoutAlignment.END));

        //sDividerMode.put(END, DirectionalLayout.);
        //sDividerMode.put(MIDDLE, DirectionalLayout.SHOW_DIVIDER_MIDDLE);
        //sDividerMode.put(BEGINNING, DirectionalLayout.SHOW_DIVIDER_BEGINNING);

        sEllipsizeMode.put(END, Text.TruncationMode.ELLIPSIS_AT_END);
        sEllipsizeMode.put(START, Text.TruncationMode.ELLIPSIS_AT_START);
        sEllipsizeMode.put(MARQUEE, Text.TruncationMode.AUTO_SCROLLING);
        sEllipsizeMode.put(MIDDLE, Text.TruncationMode.ELLIPSIS_AT_MIDDLE);

        sVisibilityMode.put(VISIBLE, Component.VISIBLE);
        sVisibilityMode.put(INVISIBLE, Component.INVISIBLE);
        sVisibilityMode.put(HIDE, Component.HIDE);

        sImagescaleMode.put(CENTER, Image.ScaleMode.CENTER);
        sImagescaleMode.put("zoom_center", Image.ScaleMode.ZOOM_CENTER);
        sImagescaleMode.put("", Image.ScaleMode.INSIDE);
        sImagescaleMode.put("zoom_start", Image.ScaleMode.ZOOM_START);
        sImagescaleMode.put("zoom_end", Image.ScaleMode.ZOOM_END);
        sImagescaleMode.put("stretch", Image.ScaleMode.STRETCH);
        sImagescaleMode.put("clip_center", Image.ScaleMode.CLIP_CENTER);

        //if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
        //    sTextAlignment.put(TEXT_ALIGNMENT_INHERIT, Component.TEXT_ALIGNMENT_INHERIT);
        //}
        //if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        //    sTextAlignment.put(TEXT_ALIGNMENT_GRAVITY, Component.TEXT_ALIGNMENT_GRAVITY);
        //    sTextAlignment.put(TEXT_ALIGNMENT_CENTER, Component.TEXT_ALIGNMENT_CENTER);
        //    sTextAlignment.put(TEXT_ALIGNMENT_TEXT_START, Text.TEXT_ALIGNMENT_TEXT_START);
        //    sTextAlignment.put(TEXT_ALIGNMENT_TEXT_END, Component.TEXT_ALIGNMENT_TEXT_END);
        //    sTextAlignment.put(TEXT_ALIGNMENT_VIEW_START, Component.TEXT_ALIGNMENT_VIEW_START);
        //    sTextAlignment.put(TEXT_ALIGNMENT_VIEW_END, Component.TEXT_ALIGNMENT_VIEW_END);
        //}
    }

    public static int parseInt(String attributeValue) {
        int number;
        if (ProteusConstants.DATA_NULL.equals(attributeValue)) {
            return 0;
        }
        try {
            number = Integer.parseInt(attributeValue);
        } catch (NumberFormatException e) {
            if (ProteusConstants.isLoggingEnabled()) {
                LogUtil.debug(TAG, attributeValue + " is NAN. Error: " + e.getMessage());
            }
            number = 0;
        }
        return number;
    }

    public static IntResult parseIntUnsafe(String s) {
        if (s == null) {
            return new IntResult("null string");
        }

        int num;
        final int len = s.length();
        final char ch = s.charAt(0);
        int d = ch - '0';
        if (d < 0 || d > 9) {
            return new IntResult("Malformed:  " + s);
        }
        num = d;

        int i = 1;
        while (i < len) {
            d = s.charAt(i++) - '0';
            if (d < 0 || d > 9) {
                return new IntResult("Malformed:  " + s);
            }
            num *= 10;
            num += d;
        }
        return new IntResult(null, num);
    }

    public static float parseFloat(String value) {
        float number = 0;
        if (null != value && value.length() > 0) {
            try {
                number = Float.parseFloat(value);
            } catch (NumberFormatException e) {
                number = 0;
            }
        }
        return number;
    }

    public static double parseDouble(String attributeValue) {
        double number;
        if (ProteusConstants.DATA_NULL.equals(attributeValue)) {
            return 0;
        }
        try {
            number = Double.parseDouble(attributeValue);
        } catch (NumberFormatException e) {
            if (ProteusConstants.isLoggingEnabled()) {
                LogUtil.debug(TAG, attributeValue + " is NAN. Error: " + e.getMessage());
            }
            number = 0;
        }
        return number;
    }

    public static int parseGravity(String value) {
        String[] gravities = value.split("\\|");
        int returnGravity = LayoutAlignment.UNSET;
        for (String gravity : gravities) {
            Primitive gravityValue = sGravityMap.get(gravity);
            if (gravityValue != null) {
                returnGravity |= gravityValue.getAsInt();
            }
        }
        return returnGravity;
    }

    public static Primitive getGravity(String value) {
        return new Primitive(parseGravity(value));
    }

    public static int parseDividerMode(String attributeValue) {
        Integer returnValue = sDividerMode.get(attributeValue);
        //        return returnValue == null ? DirectionalLayout.SHOW_DIVIDER_NONE : returnValue;
        return 0;
    }

    public static Enum parseEllipsize(String attributeValue) {
        Enum returnValue = sEllipsizeMode.get(attributeValue);
        return returnValue == null ? Text.TruncationMode.ELLIPSIS_AT_END : returnValue;
    }

    public static int parseVisibility(Value value) {
        Integer returnValue = null;
        if (null != value && value.isPrimitive()) {
            String attributeValue = value.getAsString();
            returnValue = sVisibilityMode.get(attributeValue);
            if (null == returnValue
                && (attributeValue.isEmpty() || FALSE.equals(attributeValue) || ProteusConstants
                .DATA_NULL.equals(attributeValue))) {
                returnValue = Component.HIDE;
            }
        } else //noinspection ConstantConditions
            if (value.isNull()) {
                returnValue = Component.HIDE;
            }
        return returnValue == null ? Component.VISIBLE : returnValue;
    }

    public static Primitive getVisibility(int visibility) {
        Primitive value = sVisibilityMap.get(visibility);
        return null != value ? value : sVisibilityMap.get(Component.HIDE);
    }

    public static int parseColor(String color) {
        try {
            return RgbPalette.parse(color);
        } catch (IllegalArgumentException ex) {
            if (ProteusConstants.isLoggingEnabled()) {
                LogUtil.debug(TAG, "Invalid color : " + color + ". Using #000000");
            }
            return Color.BLACK.getValue();
        }
    }

    public static boolean parseBoolean(Value value) {
        return null != value && value.isPrimitive() && value.getAsPrimitive().isBoolean() ? value.getAsBoolean()
            : null != value && !value.isNull() && Boolean.parseBoolean(value.getAsString());
    }

    public static int parseRelativeLayoutBoolean(boolean value) {
        //return value ? RelativeLayout.TRUE : 0;
        return 0;
    }

    public static void addRelativeLayoutRule(Component view, int verb, int anchor) {
        DependentLayout.LayoutConfig layoutConfig = new DependentLayout.LayoutConfig(view.getLayoutConfig());
        if (anchor != 0) {
            layoutConfig.addRule(verb, anchor);
        } else {
            layoutConfig.addRule(verb);
        }
        view.setLayoutConfig(layoutConfig);
    }

    public static int parseTextStyle(String attributeValue) {
        int typeface = Font.REGULAR;
        if (attributeValue != null) {
            attributeValue = attributeValue.toLowerCase();
            switch (attributeValue) {
                case BOLD:
                    typeface = Font.BOLD;
                    break;
                case ITALIC:
                    //typeface = Font.ITALIC;
                    break;
                case BOLD_ITALIC:
                    //typeface = Font.BOLD_ITALIC;
                    break;
                default:
                    typeface = Font.REGULAR;
                    break;
            }
        }
        return typeface;
    }

    public static boolean isTweenAnimatorPropertyResource(String attributeValue) {
        return attributeValue.startsWith(TWEEN_LOCAL_RESOURCE_STR);
    }

    /**
     * Uses reflection to fetch the R.id from the given class.
     *
     * @param variableName the name of the variable
     * @param aClass The class
     * @return resource id
     */
    public static int getResId(String variableName, Class<?> aClass) {
        Field field;
        int resId = 0;
        try {
            field = aClass.getField(variableName);
            resId = field.getInt(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resId;

    }

    /**
     * Get int resource id, by just passing the string value of id from xml file.
     * Note : This method only works for id right now
     *
     * @param fullResIdString the string id of the view
     * @return the number id of the view
     */
    public static int getOhosXmlResId(String fullResIdString) {
        if (fullResIdString != null) {
            int i = fullResIdString.indexOf("/");
            if (i >= 0) {
                String idString = fullResIdString.substring(i + 1);
                return getResId(idString, ResourceTable.class);
            }
        }
        return Component.ID_DEFAULT;
    }

    /**
     * Parses a image view scale type
     *
     * @param attributeValue attributeValue
     * @return Image.ScaleMode
     */
    public static Image.ScaleMode parsescaleMode(String attributeValue) {
        return !attributeValue.isEmpty() ? sImagescaleMode.get(attributeValue) : null;
    }

    /**
     * parses Text Alignment
     *
     * @param attributeValue value of the typeface attribute
     * @return the text alignment value
     */
    public static Integer parseTextAlignment(String attributeValue) {
        return !attributeValue.isEmpty() ? sTextAlignment.get(attributeValue) : null;
    }

    public static class IntResult {
        public final String error;
        public final int result;

        public IntResult(String error, int result) {
            this.error = error;
            this.result = result;
        }

        public IntResult(String error) {
            this.error = error;
            this.result = -1;
        }
    }
}
